// *********************************************************
//      Copyright (c) Kosta Gaitanis
//      All rights reserved
//
//      k.gaitanis@gmail.com
//
// *********************************************************

#include <QPainter>
#include <QDebug>
#include <QMouseEvent>

#include "GLWidget.h"

GLWidget::GLWidget(QWidget *parent) :
    QGLWidget(parent),
    m_pipeline(new FBOPipeline(this)),
    m_sceneFrameCounter(new FrameCounter(25, this))
{
    setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    setAutoFillBackground(false);
    connect(m_sceneFrameCounter, SIGNAL(renderingTime(double)), this, SIGNAL(sceneRenderTime(double)));
    connect(m_sceneFrameCounter, SIGNAL(frameRate(double)), this, SIGNAL(frameRate(double)));
    setFormat(QGLFormat(QGL::SampleBuffers));

    connect (m_pipeline, SIGNAL(requestPaintItems()), this, SIGNAL(requestPaint()));
    connect (m_pipeline, SIGNAL(updateGL()), this, SLOT(updateGL()));
}

GLWidget::~GLWidget()
{
}

void GLWidget::setPipelineParameter(const QString &name, const QVariant &value)
{
    m_pipeline->setPipelineParameter(name, value);
}

void GLWidget::initializeGL()
{
    m_pipeline->initialize();

    // initialize the world
    emit requestInitializeGL();
}

void GLWidget::paintGL()
{
    // start counting the time
    m_sceneFrameCounter->onNewFrameStart();

    m_pipeline->render();

    // wait until the GPU has finished all calculations
    glFinish();

    // how much time did it take?
    m_sceneFrameCounter->onNewFrameEnd();
}

void GLWidget::resizeGL(int w, int h)
{
    m_pipeline->resize(w, h);

    // this signal will repaint the openGl window whenever it is resized
    emit GLresized(w, h);
}

void GLWidget::mouseMoveEvent(QMouseEvent *event)
{
    if (event->buttons() & Qt::LeftButton) {
        QPoint delta = event->pos() - m_previousPos;
        emit moveCameraBy(delta.x(), delta.y());
        m_previousPos = event->pos();

    } else if (event->buttons() & Qt::RightButton) {
        double delta = (m_previousPos.x() - event->pos().x());
        emit rotateCameraBy(delta);
        m_previousPos = event->pos();
    }
}

void GLWidget::mousePressEvent(QMouseEvent *event)
{
    m_previousPos = event->pos();
    setFocus();
}

void GLWidget::wheelEvent(QWheelEvent *event)
{
    setFocus();
    emit zoomBy(event->delta());
}

void GLWidget::keyPressEvent(QKeyEvent *event)
{
    if (event->key() == Qt::Key_Space) {
        emit playPause();
    }else if(event->key() == Qt::Key_A){
        emit zoomBy(100);
    }else if(event->key() == Qt::Key_Z){
        emit zoomBy(-100);
    }
}

////////////////////////
/// \brief FrameCounter::FrameCounter
/// \param samples
/// \param parent
///
FrameCounter::FrameCounter(int samples, QObject *parent) :
    QObject(parent),
    m_samples(samples),
    m_frameRate(0.0)
{
}

double FrameCounter::getFrameRate() const
{
    return m_frameRate;
}

double FrameCounter::getRenderingTime() const
{
    return m_renderingTime;
}

void FrameCounter::onNewFrameStart()
{
    if (m_renderTimesList.isEmpty()) {
        m_frameRateTime.start();
    }
    m_renderTime.start();
}

void FrameCounter::onNewFrameEnd()
{
    m_renderTimesList.append(m_renderTime.elapsed());

    if (m_renderTimesList.count() >= m_samples) {
        double sum = 0;
        foreach(double t, m_renderTimesList) {
            sum += t;
        }

        m_frameRate =  1000 * m_renderTimesList.count() / m_frameRateTime.elapsed();
        m_renderingTime = sum / m_renderTimesList.count();

        m_renderTimesList.clear();

        emit frameRate(getFrameRate());
        emit renderingTime(getRenderingTime());
    }
}

